package TDALista;

import java.util.Iterator;

/**
 * Clase DoubleLinkedList que extiende de PositionList que contiene nodos que tienen referencia al anterior y al siguiente. 
 */

public class DoubleLinkedList<E>  implements PositionList<E>{
	
	protected int size;
	protected DNode<E> header;
	protected DNode<E> trailer;

 /**
  * Constructor de la clase donde se crean centinelas que controlan a la lista
  */
	public DoubleLinkedList(){
		header=new DNode<E>(null);
		trailer=new DNode<E>(null);
		header.setNext(trailer);
		trailer.setPrev(header);
		size=0;
	}


	public int size() {
		
		return size;
	}


	public boolean isEmpty() {
	
		return size==0;
	}


	public Position<E> first() {
		if(isEmpty()){
			return null;
		}
		else{
			return header.getNext();
		}
	}

	
	public Position<E> last() {
		if(isEmpty()){
			return null;
		}
		else{
			return trailer.getPrev();
		}
	}

	
	public Position<E> next(Position<E> p) throws InvalidPositionException, BoundaryViolationException{
		DNode<E> nodo=checkPosition(p);
		if(nodo.getNext()==trailer)
			throw new BoundaryViolationException("");
		return nodo.getNext();
	}
	


	
	public Position<E> prev(Position<E> p) throws InvalidPositionException, BoundaryViolationException{
		
		DNode<E> nodo=checkPosition(p);
		if(nodo.getPrev()==header)
			throw new BoundaryViolationException("");
		return nodo.getPrev();
	}

	
	public void addFirst(E element){
		DNode<E> toInsert=new DNode<E>(element);
		DNode<E> next=header.getNext();
		header.setNext(toInsert);
		next.setPrev(toInsert);
		toInsert.setNext(next);
		toInsert.setPrev(header);
		size++;
		
	}

	
	public void addLast(E element) {
		DNode<E> toInsert=new DNode<E>(element);
		DNode<E> prev=trailer.getPrev();
		trailer.setPrev(toInsert);
		prev.setNext(toInsert);
		toInsert.setNext(trailer);
		toInsert.setPrev(prev);
		size++;
		
	}

	
	public void addAfter(Position<E> p, E element)
			throws InvalidPositionException {
		DNode<E> nodo=checkPosition(p);
		DNode<E> toI=new DNode<E>(element);
		nodo.getNext().setPrev(toI);
		toI.setNext(nodo.getNext());
		toI.setPrev(nodo);
		nodo.setNext(toI);
		size++;
		
	}

	
	public void addBefore(Position<E> p, E element)
			throws InvalidPositionException {
		
		DNode<E> nodo=checkPosition(p);
		DNode<E> toI=new DNode<E>(element);
		nodo.getPrev().setNext(toI);
		toI.setPrev(nodo.getPrev());
		toI.setNext(nodo);
		nodo.setPrev(toI);
		size++;
	}

	
	public E remove(Position<E> p) throws InvalidPositionException {
		
		DNode<E> nodo=checkPosition(p);
		E toR=p.element();
		nodo.getPrev().setNext(nodo.getNext());
		nodo.getNext().setPrev(nodo.getPrev());
		nodo.setNext(null);
		nodo.setPrev(null);
		size--;	
		return toR;
	}

	
	public E set(Position<E> p, E element) throws InvalidPositionException {
		
		DNode<E> nodo=checkPosition(p);
		E toR=p.element();
		nodo.setElement(element);
		return toR;
	}

	public Iterator<E> iterator() {
		
		return new ElementIterator<E>(this);
	}

	
	public Iterable<Position<E>> positions() {
		
		PositionList<Position<E>> P=new DoubleLinkedList<Position<E>>();
		if(!isEmpty()){
			Position<E> p= first();
			while(p!=last()){
				P.addLast(p);
				try {
					p=next(p);
				} catch (InvalidPositionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (BoundaryViolationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			P.addLast(p);
		}
		return P;
	}
	
	/**	 
	 * Chequea si la posicion es valida
	 * @return El nodo donde apuntaba la posicion pasaba por parametro
	 * @exception InvalidPositionException cuando la posicion pasada por parametro es invalida
	 *  
	 */
	
	protected DNode<E> checkPosition(Position<E> p)throws InvalidPositionException{
		DNode<E> toR=null;
		
		if((p==null)||(p==header)||(p==trailer) || (isEmpty()))
			throw new InvalidPositionException("");
		else
			try{
				toR=(DNode<E>) p;
			}catch(ClassCastException e){
				e.printStackTrace();
				}
		return toR;
	}
	
	

}
